import math
import random
import matplotlib.pyplot as plt
import pandas as pd

#流程：直接密度可达   1.遍历点 2.算邻域 3.判断是否为核心点 -- 是:建立簇，邻域内全部标记-->4 ，否:略过，为噪声点-->1
# 4.遍历邻域各点-2-3-是核心点：标记-->4，否：标记为边界点

# 数据定义
# 标记数据属于哪一个聚簇
dataMark = []
# eps
eps = 0.1 # 距离
MinPts = 3 # 大于该个数，为簇
mark = 1  # 标记 0 1 2 3 ...

#初始化坐标数据簇标记 全0
def init_mark(lenData):
    for a in range(lenData):
        dataMark.append(0)

#算一个点到某一点的距离
def lenP2P(point1,point2):
    return math.sqrt( ( point1[0] - point2[0] ) ** 2 + ( point1[1] - point2[1] ) ** 2 )

# 递归 -- 是核心点:   算邻域点    -- 邻域点:核心点: 标记 --  邻域点:核心点: 标记 --
#     -- 是边界点: 略过(1轮)           |- 边界点: 标记        |- 边界点: 标记
#     -- 是噪声点: 略过                |- 噪声点: 不标记      |- 噪声点: 不标记
#递归标记
def recursionMark( areaPs , dataAll ):  # 遍历一邻域除了核心点的所有点
    for a in areaPs: # 遍历邻域所有点,不包括原点
        areaPointsTmp = areaPoints( dataAll , a) # a的邻域所有点
        if isCore( areaPointsTmp ) == 1: # 若该点是核心点
            # 标记--
            markData(areaPointsTmp)
            return recursionMark( areaPointsTmp , dataAll )
        elif isCore( areaPointsTmp ) == 2: # 若该点是边界点
            # 标记--
            markData(areaPointsTmp)

# 第一轮:从该点开始  开始函数！
def startDbscan( dataAll ):
    # dataMark[0] = 1 # 从首个点开始
    # areaPs = areaPoints( dataAll , 0 ) # 第一个点的邻域
    global mark
    for a in range( 0 , len(dataAll) ): # 遍历所有点
        if dataMark[a] == 0 : # 没被标记过进行操作
            areaPs = areaPoints( dataAll , a ) # 其邻域
            if isCore(areaPs) == 1: # 根据邻域内点个数判断类型  # 形成了簇
                dataMark[a] = mark # 该点是核心点，形成簇
                markData(areaPs) # 给该邻域所有点标记
                recursionMark(areaPs,dataAll) # 递归其邻域的邻域
                mark = mark + 1 # 使用了一轮mark
    xttmmpp = 2

# 画图 , 根据mark绘图 ,
def point_Mark( dataAll , figureName , notPause , isShow):
    global mark
    xListAll = []
    yListAll = []
    for aMark in range( 0 , mark ):
        xList = []
        yList = []
        for bNum in range( 0 , len(dataAll) ):
            if dataMark[bNum] == aMark: # 取数据中某一标记下所有x , y
                xList.append( dataAll[bNum][0] )
                yList.append( dataAll[bNum][1] )
        xListAll.append( xList )
        yListAll.append( yList )
    llo = [ 'ok' , 'or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
    #plt.figure()
    for a in range( 0 , len( xListAll ) ):
        for b in range( 0 , len( xListAll[a]) ):
            plt.plot( xListAll[a][b] , yListAll[a][b] ,llo[a] )
    plt.xlabel("longitude")
    plt.ylabel("latitude")
    plt.title( figureName )
    if isShow == 1:
        plt.show()
    if notPause == 1: # 显示时间1s
        plt.pause(1)

# 标记
def markData( dataPoints ):
    for a in dataPoints:
        dataMark[a] = mark

#算某一点的邻域内的点  dataAll:所有点,二维数组  x:第x个点
def areaPoints( dataAll , x):
    tmpCul = []
    tmpSum = 0
    for a in range(0,len(dataAll)):
        dAll_x = dataAll[x]
        dAll_a = dataAll[a]
        tmpA = a
        tmpX = x
       # if isEps( lenP2P( dataAll[x],dataAll[a]) ) and a != x and dataMark[a] == 0 : # 判断在邻域内，没被标记过
        if isEps( lenP2P( dataAll[x],dataAll[a]) ):
            if a != x:
                if dataMark[a] == 0 :
                    tmpCul.append(a)
    #         tmpSum += tmpSum
    # if tmpSum >= MinPts: # 可作为簇
    #     for a in tmpCul:
    #         dataMark[a] = mark
    errorHe = 1
    return tmpCul

#判断是否为核心点 ， 邻域内点的个数 >= MinPts
def isCore(areaPoints):
    if len(areaPoints) >= MinPts: # 是核心点
        return 1
    elif len(areaPoints) > 0 : # 是边界点
        return 2
    else: return 0 # 是噪声点

#判断距离是否小于eps
def isEps(length):
    if length <= eps :
        return 1
    return 0



def init_data(num, min, max):
    data = []
    for i in range(num):
        data.append([random.randint(min, max), random.randint(min, max)])
    return data

# 恢复全局变量
def recoverGlobal():
    global mark
    global  dataMark
    mark = 1
    dataMark = []

#读数据集
def readFileExclel(fileName):
    # fileName = "F:\\abcd\\课\\智能设计\\shangHai\\community\\20220306_geo.xlsx"
    # df = pd.read_excel(fileName) # 读取所有数据
    df = pd.read_excel(fileName,sheet_name="Sheet1",usecols=[6,7])
    data = df.values
    # print("获取到的所有值:\n{}".format( data ))
    xyList = []
    for a in range( 0 , len( data ) ):
        xyList.append( data[a] ) # 经度
    # 处理fileName = 2022-03-06
    figureName = list( fileName.split('\\')[-1].split('_')[0] )
    figureName.insert(6,'-')
    figureName.insert(4,'-')
    return ''.join( figureName ) , xyList
if __name__ == '__main__':
    # data_set1 = init_data(20, 0, 30)
    # data_set2 = init_data(20, 40, 60)
    # data_set3 = init_data(20, 70, 100)
    # data_set = data_set1 + data_set2 + data_set3
    #print(len(data_set1))
    # init_mark(len(data_set))
    #print(len(dataMark))

    #开始函数
    #startDbscan(data_set)
    #point_Mark(data_set) # 已经标记完，进行画图

    #测试1
    #测试读取数据集
    # fileName = "F:\\abcd\\课\\智能设计\\shangHai\\community\\20220308_geo.xlsx"
    # ( figureName , data_set ) = readFileExclel( fileName )
    # init_mark(len(data_set))
    # startDbscan(data_set)
    # point_Mark(data_set , figureName , 0 , 1) # 已经标记完，进行画图
    #------------------------以上测试---------------------------------------------

    # 例子1. 累积绘制
    # plt.figure() # 画板 , 若放在绘制函数外 , 未设置画布则会累积绘制
    # plt.ion() # 打开交互模式
    # fileNameF = "F:\\abcd\\课\\智能设计\\shangHai\\community\\202203"
    # for date in range( 6 , 15): # 3-6 -- 3-14
    #     fileName = fileNameF + str(date).zfill(2) + "_geo.xlsx"
    #     ( figureName , data_set ) = readFileExclel( fileName )
    #     init_mark(len(data_set))
    #     startDbscan(data_set)
    #     point_Mark(data_set , figureName , 1 , 1 ) # 已经标记完，进行画图
    #     recoverGlobal()
    # plt.clf() # 清楚图像

    # 例子2. 绘制到8张图上
    # fig = plt.figure(1) # 创建画板1
    plt.figure(dpi=100 , figsize=(24,24))
    plt.subplots_adjust(left=None, bottom=None, right=None, top=None,wspace=1, hspace=1)
    noPlot = 1
    fileNameF = "F:\\abcd\\课\\智能设计\\shangHai\\community\\202203"
    for date in range( 6 , 15): # 3-6 -- 3-14
        ax1 = plt.subplot(3, 4, noPlot )
        fileName = fileNameF + str(date).zfill(2) + "_geo.xlsx"
        ( figureName , data_set ) = readFileExclel( fileName )
        init_mark(len(data_set))
        startDbscan(data_set)
        point_Mark(data_set , figureName , 0 , 0) # 已经标记完，进行画图
        recoverGlobal()
        noPlot += 1
    plt.show()



